#include "ul_malloc.h"
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include "ul_log_adapter.h"
using namespace LinuxNetworkUtility;
namespace 
{
  UlMemoryPool *g_memory_pool = NULL;
  UlTimer *g_time = NULL;
  UlModSet          *g_mod_set = NULL;
  static int64_t    g_gc_frenquency_us = 0;
  pthread_key_t     g_thread_memory_pool_key;
  pthread_once_t    g_thread_memory_pool_once = PTHREAD_ONCE_INIT;

  void destroy_thread_memory_pool(void *ptr)
  {
    UlThreadMemoryPool *pool = reinterpret_cast<UlThreadMemoryPool*>(ptr);
    delete pool;
    pool = NULL;
  }

  void destroy_main_thread_memory_pool()
  {
    UlThreadMemoryPool *pool  = NULL;
    pool = reinterpret_cast<UlThreadMemoryPool*>(pthread_getspecific(g_thread_memory_pool_key));
    delete pool;
    pool = NULL;
  }

  void make_thread_memory_pool_key()
  {
    pthread_key_create(&g_thread_memory_pool_key, destroy_thread_memory_pool);
  }


  UlThreadMemoryPool *get_thread_memory_pool()
  {
    static __thread UlThreadMemoryPool *instance = NULL;
    static __thread int thread_specific_setted = 0;
    pthread_once(&g_thread_memory_pool_once, make_thread_memory_pool_key);
    if (NULL == instance)
    {
      if (NULL != g_memory_pool)
      {
        instance = new(std::nothrow)UlThreadMemoryPool(*g_memory_pool, *g_mod_set, *g_time, 
                                                       g_gc_frenquency_us);
        if (NULL == instance)
        {
          UL_LOG_ERROR("fail to allocate memory for thread memory pool [errno:%d]", errno);
        }
      }
      else
      {
        UL_LOG_ERROR("initialize global memory pool first");
      }
    }
    else
    {
      /// do nothing
    }
    if (!thread_specific_setted && NULL != instance)
    {
      if (pthread_setspecific(g_thread_memory_pool_key, instance) == 0)
      {
        thread_specific_setted = true;
      }
    }
    return instance;
  }
}

int LinuxNetworkUtility::ul_init_malloc(UlMemoryPool & global_pool, UlTimer &time, 
                                        UlModSet & mod_set, const int64_t thread_pool_gc_frenquency_us)
{
  g_time = &time;
  g_mod_set = &mod_set;
  g_gc_frenquency_us = thread_pool_gc_frenquency_us;
  g_memory_pool = &global_pool;
  atexit(destroy_main_thread_memory_pool);
  return 0;
}

void *LinuxNetworkUtility::ul_malloc(const int64_t size, const int32_t mod_id, int64_t *got_size, 
                                     const UlMemoryFreeFlag flag)
{
  void *result = NULL;
  UlThreadMemoryPool *thread_pool = get_thread_memory_pool();
  if (NULL != thread_pool)
  {
    result = thread_pool->malloc(size,mod_id,got_size,flag);
  }
  else
  {
    if (NULL != got_size)
    {
      *got_size = 0;
    }
  }
  return result;
}

void LinuxNetworkUtility::ul_free(void *ptr)
{
  UlThreadMemoryPool *thread_pool = get_thread_memory_pool();
  if (NULL != thread_pool)
  {
    return thread_pool->free(ptr);
  }
}

void LinuxNetworkUtility::ul_safe_free(void *& ptr)
{
  ul_free(ptr);
  ptr = NULL;
}



LinuxNetworkUtility::UlMemBuffer::UlMemBuffer()
{
  buf_size_ = 0;
  buf_ptr_ = NULL;
}

LinuxNetworkUtility::UlMemBuffer::UlMemBuffer(const int64_t nbyte, const int32_t mod_id, 
                                              const UlMemoryFreeFlag flag)
{
  buf_size_ = 0;
  buf_ptr_ = NULL;
  buf_ptr_ = ul_malloc(nbyte,mod_id,&buf_size_,flag);
}

LinuxNetworkUtility::UlMemBuffer::~UlMemBuffer()
{
  if (NULL != buf_ptr_)
  {
    ul_safe_free(buf_ptr_);
  }
  buf_size_ = 0;
}

void *LinuxNetworkUtility::UlMemBuffer::malloc(const int64_t nbyte, const int32_t mod_id, 
                                               const UlMemoryFreeFlag flag )
{
  if (nbyte <= buf_size_ && buf_ptr_ != NULL)
  {
  }
  else
  {
    ul_safe_free(buf_ptr_);
    buf_size_ = 0;
    buf_ptr_ = ul_malloc(nbyte,mod_id,&buf_size_,flag);
  }
  return buf_ptr_;
}

void * LinuxNetworkUtility::UlMemBuffer::get_buffer()
{
  return buf_ptr_;
}

int64_t LinuxNetworkUtility::UlMemBuffer::get_buffer_size()
{
  return buf_size_;
}
